alarm chrono



Custom keyboard V2


Created: 2024-01-24 | Modified: 2024-11-02

Adjustable column position, thumb clusters

Left side working prototype

Checkout V1 of this keyboard.

Parts list

With the first edition i validated that i like ortholinear layout, this enables me to offset the columns relative to each other, but i cannot settle on how much to offset it, therefore i got the idea to make it adjustable, I've not seen this done before.

Notes idea Notes column module idea generation

The keyboard base will have GPIO + I²C pulled out on a connector, allowing any kind of expansion I'll make the following modules: trackpoint, mouse buttons, thumb clusters, rollers for vertical scrolling and volume.

Desiging a switch holder

Switch holder itterations

Trackpoint

Trackpoint modules

The trackpoint module is a PS/2 device, see QMK documentation and Trackpoint pinout

Height

Key switch height compared to v1

Keycaps

3D printed keycaps with legends

To print legends, multi color on the same layer see this cool blog: Creating a Keyboard - docs.juliaebert.com

Switches

Switches

product page

3D printed trace groves

I ought to use a PCB for this, but that doesn't play nice with my rapid proto typing.

Slide test 1 Slide test 1 slicer 2 lines

Testing if this method can be manufactured on my 3D printer, it's only 2 lines wide.

3D printed "PCB" feasability

3D printed "PCB" feasability test

Other keyboards with adjustable columns

Apparently I'm not the first to think of adjustable columns, bummer see this ReSummit/Seismos GitHub repo And this nezumee/zebra GitHub repo

Design goals

Meta

This article is more of a timeline of how it actually happened, by meerly appending, instead of going back, and changing stuff

Column module prototype

2 column modules Column module nail indent Column module slider foot End stop back Screws Screws side view

Printing Keycaps

3d printing keycaps sideways Keycaps itterations Again, printed on its side for greater dimensional accuracy

Thumb cluster design vision

Came across this really cool keyboard called Dygma Defy youtube.com - video dygma.com - Blog post they show their design iterations on thumb clusters they seem to align with what i envisioned, namely ergonomically shaped buttons, which is possible due to the 3D printing manufacturing. They've done lots

GPIO expander chip ordered

Ordered an MCP23017 I²C GPIO expander in DIP28 package format. To use on the left side of the keyboard.

Layout designed

Keyboard layout

This layout avoids having 2 unit wide buttons, which would interfere with the column sliding mechanism. Instead these buttons are moved to 2 thumb clusters. Which i I'm eager to try out.

Prototype pile

Prototype pile

Here's the pile of prototypes it took to get a satisfying and functional mechanism and keycaps.

Module wired

Module wired top Module wired bottom

Soldered up the first module. I used stranded wire due to metal fatigue. This methodology seems to be feasible. I'm going to add pin header connectors to connect it to the board, so it can be disconnected and the module removed. Spanning the top is gonna be a prototype board PCB with all the diodes, connectors for modules, DIP28 GPIO expander and the TRRS headphone jack.

Modules for left side printed

Coulumns modules for left side WIP

Done printing and cleaning up all the modules for the left side.

Left side modules in base top Left side modules in base bottom

Printed the bottom for the left side it unfortunately bent slightly when the modules were put in place, even tho i was patient letting it cool down after printing. I held it over the stove, and counter bend it. Then placed it on a flat surface. Which fixed the curve.

Left side wiring diagram

Planned out the circuit diagram for the left side of the keyboard, everything fit really nicely. I'm excited to make it a reality. The top features pin headers for each sliding module. Which has 6 pins, 1 for the column, and 5 for the rows. The rows are connected to the diodes, which bridge the gap to their respective colored bus bars on the bottom side. Which forms a natural ╲ ╲ ╲ ╲ ╲ pattern. The columns have busbars on the bottom left side. Both bus bar clusters are terminated in 2 flex cable connections, one for the GPIO expander chip, and one for a male pin header for testing and debugging, it's also pin compatible with my old keyboard, allowing me to do an agile partial goal, and get success feeling of trying it out, motivating me to keep working, and getting to test it before i add to many variables and unknowns, which could lead to multiple failure points, making me have to debug multiple possible error sources at the same time. There's 2 rows of connection points next to the debug connector, which is a fine place to extend the connection on to thumb cluster module, which differs because it has the diodes on the module as opposed to on the circuit board. It has 1 pin for the row, and the rest for the columns. Because it's wired up like it was a row at the bottom of the keyboard. Finally the headphone jack fits in the top right corner, pointing upwards, since there's already going to be the cables from the sliding modules looping that way.

Circuit board initial test

Circuit board painted Circuit initial test

Painted the circuit board, to indicate where the busbars on the bottom are routed. Wired up a test setup to test the first 4x4 buttons, and it works, yay ^-^

Left side working prototype

Full stop

So up to this point, i had the intention of making this into a full hand wired keyboard. But I don't like how the wires at the top ended up looking, so I've chosen to cut my losses and use my prototype to decide how I want the offsets to be, and as a test bed to remove my last few unknowns, namely, thumb cluster and I/O expander chip. I should have cut more corners, to test the core concept, before spending time, on the niceties.

MCP23017

Using this tutorial: makerhacks.com ─ Use the MCP23017 GPIO Port Expander to Add 16 IO Pins i was able to make the MicroPython example work, but issues arise. It keeps throwing the exception: [Errno 5] EIO, furthermore i2c.scan() is not consistent with what it returns. All this is despite forgoing the breadboard, in favor of directly soldered connections :/

MCP23017 MicroPython code snippet

# https://blog.node5.net/Custom%20keyboard%20V2#mcp23017-micropython-code-snippet
# Based on: https://www.makerhacks.com/mcp23017#h-using-mcp23017-with-raspberry-pi-pico-and-micropython
import time
import machine

i2c = machine.I2C(1,
                  sda=machine.Pin(0),
                  scl=machine.Pin(1),
                  )
i2cdevices = i2c.scan()
print(i2cdevices)
time.sleep(0.50)
i = 0
while 1:
    try:
        i2c.writeto_mem(0x20, 0x00, b'\xFF')
        i2c.writeto_mem(0x20, 0x01, b'\x00')
        i2c.writeto_mem(0x20, 0x13, b'\x00')
        while 1:
            i2c.writeto_mem(0x20, 0x13, b'\xFF')
            time.sleep(0.50)
            i2c.writeto_mem(0x20, 0x13, b'\x00')
            time.sleep(0.50)
    except Exception as ex:
        i += 1
        print(i, ex)

github.com ─ dhylands / rshell Can be used to make it easier, to work with MicroPython

The issues persisted, and i decided to move from MicroPython to C++ using the Adafruit library And since the github.com ─ earlephilhower/arduino─pico functions bool SetSDA and bool SetSCL didn't work to change I²C 0 pins, i change to the default pins GP4 for SDA and GP4 for SCL denoted by the Raspberry PI Pico pinout.

Raspberry Pi Pico pinout

Still the issues persisted, still working sporadically for a bit, and then stopped working. I then tried using pull─up resistors with a value of 4.7KΩ. Still didn't work consistently. I then searched around, and came across this forum post: forum.arduino.cc ─ Problems with MCP23017 ─ Solved And i followed the wiring described therein. Namely, tie A0, A1 and A2 to ground, Reset to 3.3v using a 1KΩ pull-up resistor, and finally tie SDA and SCL to 3.3v using a 1KΩ pull-up resistor (see the following ascii art diagram)

MCP23017 wiring diagram

                            ┌── U ──┐
[ LED Anode ]   GPB0 ( 8 )  1    28 GPA7 ( 7 )
                GPB1 ( 9 )  2    27 GPA6 ( 6 )
                GPB2 ( 10 ) 3    26 GPA5 ( 5 )
                GPB3 ( 11 ) 4    25 GPA4 ( 4 )
                GPB4 ( 12 ) 5    24 GPA3 ( 3 )
                GPB5 ( 13 ) 6    23 GPA2 ( 2 )
                GPB6 ( 14 ) 7    22 GPA1 ( 1 )
                GPB7 ( 15 ) 8    21 GPA0 ( 0 )
[ 3.3v ]               VDD  9    20 INTA
[ GND ]                VSS  10   19 INTB  
                       NC   11   18 RESET  [ 1  3.3v ]
[ GP5 & 1  3.3v ]   SCL  12   17 A2     [ GND ]
[ GP4 & 1  3.3v ]   SDA  13   16 A1     [ GND ]
                       NC   14   15 A0     [ GND ]
                            └───────┘

( x ) = Adafruit library number for this pin
[ x ] = Microcontroller pin

MCP23017 C++ code snippet

// https://blog.node5.net/Custom%20keyboard%20V2#mcp23017-c-code-snippet
// Based on: https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library/blob/master/examples/mcp23xxx_blink/mcp23xxx_blink.ino
// Blinks an LED attached to a MCP23017 pin.

#include <Adafruit_MCP23X17.h>

#define LED_PIN 8     // MCP23XXX pin LED is attached to

Adafruit_MCP23X17 mcp;

void setup() {
    Serial.begin(9600);
    // while (!Serial);  // Wait for Serial connection, enable for debug of early code
    Serial.println("MCP23017 Blink Test!");

    // Find ID, by attempting initialization sequentially
    int i = 0;
    while (!mcp.begin_I2C(i)) {
        Serial.print("Error with ID:");
        Serial.println(i);
        i++;
        delay(10);
    }
    Serial.print("Success with ID:");
    Serial.println(i);

    mcp.pinMode(LED_PIN, OUTPUT);

    Serial.println("Looping...");
}

void loop() {
    Serial.print("High, millis: ");
    Serial.println(millis());
    mcp.digitalWrite(LED_PIN, HIGH);
    delay(250);

    Serial.print("Low,  millis: ");
    Serial.println(millis());
    mcp.digitalWrite(LED_PIN, LOW);
    delay(250);
}

Thumb cluster prototype iterations

I've iterated on the thumb cluster design (based on the amazing work by the Dygma Defy team) and I've reached a design that works for me.

Thumb cluster CAD based on Dygma Defy Thumb cluster itterations

MCP23017 breadboard TRRS cable

MCP23017 breadboard TRRS cable

I've now moved to using 2 separate breadboards to test if the I²C is stable over the cable (it seems to be). Pulling the TRRS cable while powered, doesn't break it, but does require a power cycle to function again.

MCP23017 button code

// https://blog.node5.net/Custom%20keyboard%20V2#mcp23017-button-code
// Based on: https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library/blob/master/examples/mcp23xxx_combo/mcp23xxx_combo.ino
#include <Adafruit_MCP23X17.h>


#define BLINK_FREQUENCY 1000 // Milliseconds
#define LED_PIN         8    // LED pin on MCP23017
#define BUTTON_PIN      7    // Button pin on MCP23017

Adafruit_MCP23X17 mcp;
int blink_frequency; // Milliseconds

void setup() {
    Serial.begin(9600);
    //while (!Serial);
    Serial.println("MCP23017 Blink Test!");
    int i = 0;
    while (!mcp.begin_I2C(i)) {
        Serial.print("Error with ID:");
        Serial.println(i);
        i++;
        delay(10);
    }
    Serial.print("Success with ID:");
    Serial.println(i);

    mcp.pinMode(LED_PIN, OUTPUT);
    mcp.pinMode(BUTTON_PIN, INPUT_PULLUP);

    Serial.println("Looping...");
}

void loop() {
    // What is an interupt?
    if (!mcp.digitalRead(BUTTON_PIN)) {
        // Button pushed, light LED
        blink_frequency = BLINK_FREQUENCY / 20;
    } else {    
        blink_frequency = BLINK_FREQUENCY; // Milliseconds
    }
    // Alternate state every blink_frequency milliseconds
    bool state = (millis() % (blink_frequency * 2)) > blink_frequency;
    Serial.print("State: ");
    Serial.println(state);
    mcp.digitalWrite(LED_PIN, state);
}

Other QMK keyboards using MCP23017

MCP23017 working principle has been tested. Now it's time to make it work with QMK. The following keyboards implement the MCP23017.

# List keyboards containing mcp23017 case insensitive
grep -I --color=always -rnie "mcp23017" | cut -d'/' -f2 | uniq

# List files containing mcp23017 case insensitive with the file extension .h or .c
grep -I --color=always -rnie "mcp23017" --include \*.h --include \*.c | cut -d':' -f1 | uniq

PCB Design

Based on this tutorial: How to Design Mechanical Keyboard PCBs with Kicad, Joe Scotto - youtube.com

LED Indicator

I'm adding an indicator LED, it's an RGB LED, meaning I'll have to add a current limiting resistor per color, with a unique value for each. I then run into the issue that the forward voltage of the blue LED is higher than the supply voltage, which is about 3v. What will happen to an LED if the forward voltage (Vf) is greater than its supply voltage? - electronics.stackexchange.com


Comments













(Will await approval before becoming public)